﻿using System;
using System.Collections;
using System.Linq;
using ATMCableCloud.Graph;
using ATMConst;

namespace ATMCableCloud
{
	/** 
	 * singleton with connected nodes to the system
	 */
	class ConnectedAtmNodes
	{
		readonly ArrayList nodes_;
		static ConnectedAtmNodes rInstance_;
		ConnectedAtmNodes()
		{
			nodes_ = new ArrayList();
		}

		/** public methods */

		static public ConnectedAtmNodes getInstance()
		{
			if(rInstance_ == null)
			{
				// mutex 
				if(rInstance_ == null)
				{
					rInstance_ = new ConnectedAtmNodes();
				}
				// release mutex
			}
			return rInstance_;
		}
		public void deleteNode(AtmNode nodeToDelete)
		{
			foreach (var node in nodes_)
			{
				for (var i = 0; i < (node as AtmNode).links.Count;i++)
				{
				/*	Console.WriteLine("link from {0} to :{1}", (node as AtmNode).name, 
							(((node as AtmNode).links[i]) as AtmNode.PhysicalLink).remoteEndPoint.name);*/
					if (Equals(
								(((node as AtmNode).links[i]) as AtmNode.PhysicalLink).remoteEndPoint,
								nodeToDelete ))
					{
						(node as AtmNode).links.RemoveAt(i);
					}
				}
			}
			nodes_.Remove(nodeToDelete);
			//debug();
		}
		public bool find(string name, out AtmNode outNode)
		{
			outNode = null;
			foreach (var node in nodes_.Cast<AtmNode>().Where(node => node.name == name))
			{
				if (node.tcpServer.checkIsConnected())
				{
					//still connected
					outNode = node;
					return true;
				}
				node.tcpServer.socket.Close();
				nodes_.Remove(node);
				return false;
			}
			return false;
		}
		public void addNode(AtmNode node)
		{
			var index = nodes_.Add(node);
			var graphNode = NetworkGraph.getInstance().find(node.name);
			/* add all links with connected (at this time) nodes and vice versa */
			foreach (NetworkGraph.Node.Link link in graphNode.links)
				foreach (var connectedNode in getInstance().nodes_.Cast<AtmNode>().Where(connectedNode => connectedNode.name == link.remoteEndpoint.name))
					(nodes_[index] as AtmNode).links.Add(new AtmNode.PhysicalLink(
						                                     link.localPort,
						                                     link.remotePort,
						                                     connectedNode));
			foreach (AtmNode connectedNode in getInstance().nodes_)
			{
				var nodeFromGraph = NetworkGraph.getInstance().find(connectedNode.name);
				foreach (var link in nodeFromGraph.links.Cast<NetworkGraph.Node.Link>().Where(link => link.remoteEndpoint.name == node.name))
					connectedNode.links.Add(new AtmNode.PhysicalLink(
						                        link.localPort,
						                        link.remotePort,
						                        (nodes_[index] as AtmNode)));
			}

			(nodes_[index] as AtmNode).connectToSystem();
			//debug();
		}
		public int getPort()
		{
            var result = Const.DEFAULT_PORT_NUMBER + 1;
			return nodes_.Cast<AtmNode>().Aggregate(result, (current, node) => (current > node.tcpServer.port ? current : node.tcpServer.port + 1));
		}
		void debug()
		{
			foreach (var node in nodes_)
			{
				Console.WriteLine("Node:{0}", (node as AtmNode).name);
				foreach (var link in (node as AtmNode).links)
				{
					Console.WriteLine((link as AtmNode.PhysicalLink).remoteEndPoint.name);
				}
			}
		}
	}
}
